package config

import (
	"log"

	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	// "github.com/casbin/casbin/v2/persist"
	// entadapter "github.com/casbin/ent-adapter"
	// rediswatcher "github.com/casbin/redis-watcher/v2"
	xormadapter "github.com/casbin/xorm-adapter/v2"
	_ "github.com/go-sql-driver/mysql"
	// redis2 "github.com/redis/go-redis/v9"
	"github.com/zeromicro/go-zero/core/logx"
	// "github.com/zeromicro/go-zero/core/stores/redis"
)

// CasbinConf is the configuration structure for Casbin
type CasbinConf struct {
	ModelText string `json:"ModelText,optional,env=CASBIN_MODEL_TEXT"`
}

// NewCasbin returns Casbin enforcer.
func (l CasbinConf) NewCasbin(dbType, dsn string) (*casbin.Enforcer, error) {
	// Initialize a Xorm adapter and use it in a Casbin enforcer:
	// The adapter will use the MySQL database named "casbin".
	// If it doesn't exist, the adapter will create it automatically.
	// adapter, err := xormadapter.NewAdapter(dbType, dsn)
	// adapter, err := entadapter.NewAdapter(dbType, dsn)
	// logx.Must(err)

	// Or you can use an existing DB "mall_admin" like this:
	// The adapter will use the table named "casbin_rule".
	// If it doesn't exist, the adapter will create it automatically.
	adapter, err := xormadapter.NewAdapter(dbType, dsn, true)
	logx.Must(err)

	var text string
	if l.ModelText == "" {
		text = `
    [request_definition]
    r = sub, obj, act

    [policy_definition]
    p = sub, obj, act

    [role_definition]
    g = _, _

    [policy_effect]
    e = some(where (p.eft == allow))

    [matchers]
    m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
`
	} else {
		text = l.ModelText
	}

	m, err := model.NewModelFromString(text)
	logx.Must(err)

	enforcer, err := casbin.NewEnforcer(m, adapter)
	logx.Must(err)

	// Load the policy from DB.
	err = enforcer.LoadPolicy()
	logx.Must(err)

	return enforcer, nil
}

// MustNewCasbin returns Casbin enforcer. If there are errors, it will exist.
func (l CasbinConf) MustNewCasbin(dbType string, dsn string) *casbin.Enforcer {
	csb, err := l.NewCasbin(dbType, dsn)
	if err != nil {
		logx.Errorw("initialize Casbin failed", logx.Field("detail", err.Error()))
		log.Fatalf("initialize Casbin failed, error: %s", err.Error())

		return nil
	}

	return csb
}

// MustNewRedisWatcher returns redis watcher. If there are errors, it will exit.
// f function will be called if the policies are updated.
// func (l CasbinConf) MustNewRedisWatcher(c redis.RedisConf, f func(string2 string)) persist.Watcher {
// 	w, err := rediswatcher.NewWatcher(c.Host, rediswatcher.WatcherOptions{
// 		Options: redis2.Options{
// 			Network:  "tcp",
// 			Password: c.Pass,
// 		},
// 		Channel:    "/casbin",
// 		IgnoreSelf: false,
// 	})
// 	logx.Must(err)

// 	err = w.SetUpdateCallback(f)
// 	logx.Must(err)

// 	return w
// }

// MustNewCasbinWithRedisWatcher returns Casbin Enforcer with Redis watcher.
// func (l CasbinConf) MustNewCasbinWithRedisWatcher(dbType string, dsn string, c redis.RedisConf) *casbin.Enforcer {
// 	cbn := l.MustNewCasbin(dbType, dsn)
// 	w := l.MustNewRedisWatcher(c, func(data string) {
// 		rediswatcher.DefaultUpdateCallback(cbn)(data)
// 	})
// 	err := cbn.SetWatcher(w)
// 	logx.Must(err)

// 	// Save the policy back to DB.
// 	err = cbn.SavePolicy()
// 	logx.Must(err)

// 	return cbn
// }
